1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.util.file;
12 import hip.util.conv:to;
13 import hip.util.array:join, array;
14 import hip.util.system;
15 import hip.util.path;
16 import hip.util.string;
17 
18 
19 string getFileContent(string path, bool noCarriageReturn = true)
20 {
21     import core.stdc.stdio;
22     path = sanitizePath(path);
23     FILE* file = fopen((path~"\0").ptr, "r");
24     if(!file)
25         return "";
26     char[] buffer;
27 
28     fseek(file, 0, SEEK_END);
29     auto size = ftell(file);
30     fseek(file, 0, SEEK_SET);
31 
32     buffer.length = cast(typeof(buffer.length))size;
33     size_t readSize = fread(buffer.ptr, cast(size_t)size, 1, file);
34     if(readSize != buffer.length)
35         buffer.length = readSize;
36     fclose(file);
37 
38     string content = cast(string)buffer;
39     return (noCarriageReturn) ? content.replaceAll('\r') : content;
40 }
41 
42 version(Windows)
43 {
44     string getcwd()
45     {
46         import core.sys.windows.winnt:MAX_PATH;
47         import core.sys.windows.winbase;
48         char[MAX_PATH] buffer;
49 
50         uint length = GetCurrentDirectoryA(MAX_PATH, buffer.ptr);
51 
52         char[] ret = new char[](length);
53         ret[] = buffer[0..length];
54 
55         return cast(string)ret;
56     }
57 }
58 else
59 {
60     string getcwd()
61     {
62         import std.file;
63         return std.file.getcwd();
64     }
65 }
66 
67 
68 
69 string stripLineBreaks(string content)
70 {
71     content = content.replaceAll('\r');
72     content = content.replaceAll('\n');
73     return content;
74 }
75 
76 // string getFileContentFromBasePath(string path, string basePath, bool noCarriageReturn = true)
77 // {
78 //     string finalPath = relativePath(sanitizePath(path), sanitizePath(basePath));
79 //     return getFileContent(finalPath, noCarriageReturn);
80 // }
81 
82 
83 
84 version(none)
85 {
86     import std.stdio:File;
87 
88     version(CustomRuntimeTest) version = CustomRuntime;
89     version(WebAssembly) version = CustomRuntime;
90     version(PSVita) version = CustomRuntime;
91 
92     version(CustomRuntimeTest) {void fileTruncate(File file, ptrdiff_t offset){}}
93     else
94     {
95         import std.file;
96         void fileTruncate(File file, ptrdiff_t offset) 
97         {
98             version (Windows) 
99             {
100                 import hip.util.windows;
101                 file.seek(offset);
102                 if(!SetEndOfFile(file.windowsHandle()))
103                     throw new FileException(file.name, "SetEndOfFile error");
104             }
105 
106             version (Posix) 
107             {
108                 import core.sys.posix.unistd: ftruncate;
109                 int res = ftruncate(file.fileno(), offset);
110                 if(res != 0)
111                     throw new FileException(file.name, "ftruncate error with code "~to!string(res));
112             }
113         }
114     }
115 }
116 
117 version(none) class FileProgression
118 {
119     protected ulong progress;
120     protected uint stepSize;
121     protected ulong fileSize;
122     protected void delegate(ref ubyte[] data) onFinish;
123     protected void delegate(float progress) onUpdate;
124     ubyte[] fileData;
125     ubyte[] buffer;
126     File target;
127 
128     /**
129     *   If bytes == 0, it will use readsteps.
130     *
131     *   Readsteps default is by progressing from 0 to 100, which makes great for percentage. Also notice that with 100 readsteps there's almost
132     *   no loss compared to reading the file in one go, so it is the recommended value.
133     *
134     *   The greater the readsteps, the more time it will take to read the file and the more precision the percentage will have. Usually 100 should be enough.
135     *
136     *   If the readsteps are greater than the filesize, it will clamp to fileSize as readsteps.
137     *   That means it will update at every byte
138     */
139     this(string filePath, uint readSteps = 100, uint bytes = 0)
140     {
141         assert(readSteps != 0 || bytes != 0, "Can't have readSteps and bytes both == 0");
142         progress = 0;
143         target = File(filePath, "r");
144         ulong fSize = fileSize = target.size;
145         assert(cast(uint)fSize < uint.max, "Filesize is greater than uint.max, contact the FileProgression mantainer");
146         fileData.reserve(cast(uint)fSize);
147         
148         if(readSteps > fSize)
149             readSteps = cast(uint)fSize;
150         if(bytes != 0)
151             readSteps = cast(uint)fSize/bytes;
152 
153         real sz =cast(real)fSize/readSteps;
154         if(sz != fSize/readSteps) //Odd
155         {
156             size_t remaining = cast(size_t)((sz-(fSize/readSteps))*readSteps);
157             buffer = new ubyte[remaining];
158             target.rawRead(buffer);
159             fileData~= buffer[];
160             progress+= remaining;
161             stepSize = cast(uint)(fSize-remaining)/readSteps;
162         }
163         else
164             stepSize = cast(uint)fSize/readSteps;
165         buffer.length = stepSize;
166     }
167 
168     void setOnFinish(void delegate(ref ubyte[] data) onFinish){this.onFinish = onFinish;}
169     void setOnUpdate(void delegate(float progress) onUpdate){this.onUpdate = onUpdate;}
170 
171     bool update()
172     {
173         target.rawRead(buffer);
174         fileData~= buffer[];
175         progress+=stepSize;
176         if(onUpdate)
177             onUpdate(getProgress());
178         bool finished = progress >= fileSize;
179         if(finished)
180         {
181             target.close();
182             if(onFinish)
183                 onFinish(this.fileData);
184         }
185 
186         return !finished;
187     }
188     
189 
190     float getProgress(){return progress/cast(float)fileSize;}
191     @property ulong readSize(){return fileData.length;}
192     @property ulong size(){return fileSize;}
193 
194 
195     override string toString(){return cast(string)fileData;}
196 }